#include<sys/socket.h>
#include<stdio.h>
#include<errno.h>
#include<netinet/in.h>
#include<string.h>
#include<pthread.h>
#include <sys/select.h>
#include<poll.h>
#include<sys/epoll.h>

#define BUFFER_LENGTH 1024
int recv_cb(int fd);
int send_cb(int fd);
int accept_cb(int fd);

typedef int (*RECALLBACK)(int fd);
int epfd = 0;
struct conn_item
{
    int fd;
    char buffer[BUFFER_LENGTH];
    int idx;

    char rbuffer[BUFFER_LENGTH];
    int rlen;
    char wbuffer[BUFFER_LENGTH];
    int wlen;

   union{ RECALLBACK accept_callback;
    RECALLBACK recv_callback;}recv_t;
    RECALLBACK send_callback;
};
struct conn_item connlist[1024] = {0};


int set_event(int fd , int event ,int flag)
{
    if(flag){
         struct epoll_event ev;
    ev.events = event;
    ev.data.fd = fd;
    epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&ev);

    }else{
        struct epoll_event ev;
    ev.events = event;
    ev.data.fd = fd;
    epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&ev);

    }
}
int accept_cb(int fd)
{
    struct sockaddr_in clientaddr;
    socklen_t len = sizeof(clientaddr);
    int clientfd = accept(fd,(struct sockaddr*)&clientaddr,&len);
    printf("socked : %d,clientfd : %d\n",fd,clientfd);

    if(clientfd < 0)
    {
        return -1;
    }
    set_event(clientfd,EPOLLIN,1);

    connlist[clientfd].fd = clientfd;
    memset(connlist[clientfd].rbuffer,0,BUFFER_LENGTH);
    connlist[clientfd].rlen = 0;
     memset(connlist[clientfd].wbuffer,0,BUFFER_LENGTH);
    connlist[clientfd].wlen = 0;
    connlist[clientfd].recv_t.recv_callback= recv_cb;
    connlist[clientfd].send_callback = send_cb;
    return clientfd;

}

int recv_cb(int fd)
{
    char *buffer = connlist[fd].rbuffer;
    int idx = connlist[fd].rlen;

    int count = recv(fd,buffer+idx,BUFFER_LENGTH-idx,0);
    if(count == 0)
    {
        epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);
            close(fd);
            return -1;

    }
    connlist[fd].rlen += count;

    set_event(fd,EPOLLOUT,0);
    memcpy(connlist[fd].wbuffer,connlist[fd].rbuffer,connlist[fd].rlen);
    connlist[fd].wlen = connlist[fd].rlen;
    return count;
}

int send_cb(int fd)
{

    char *buffer = connlist[fd].wbuffer;
    int idx = connlist[fd].wlen;

    int count = send(fd,buffer,idx,0);
    set_event(fd,EPOLLIN,0);
    return count;



}

void *client_thread(void *arg)
{
    int clientfd = * (int *)arg;

    while(1)
    {char buf[128] = {0};
    int count = recv(clientfd,buf,128,0);
    if(count == -1)
    { perror("count");close(clientfd);return NULL;}
    if(count == 0)
    {close(clientfd);return NULL;}
    send(clientfd,buf,count,0);
    printf("clientfd : %d,count:  %d,buf: %s\n",clientfd,count,buf);}
    
     close(clientfd);
}


int main()
{
    struct conn_item;
    int sockfd = socket(AF_INET,SOCK_STREAM,0);

    struct sockaddr_in serveraddr;
    memset(&serveraddr,0,sizeof(struct sockaddr_in));

    serveraddr.sin_family  =  AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons(2047);

    if(-1 == (bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(struct sockaddr))))
    {
        perror("bind");
        return -1;
    }

    listen(sockfd,10);

    connlist[sockfd].fd = sockfd;
    connlist[sockfd].recv_t.accept_callback = accept_cb;

//epoll

     epfd = epoll_create(1);
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = sockfd;

    epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&ev);
    struct epoll_event events[1024] = {0};
    while(1){
        int nready = epoll_wait(epfd,events,1024,-1);
        int i= 0;
        for(i= 0;i<nready;i++)
        {
            int connfd = events[i].data.fd;
             if(events[i].events & EPOLLIN)
            {
                 int count = connlist[connfd].recv_t.recv_callback(connfd);
                 printf("recv:buffer->%s\n",connlist[connfd].rbuffer);
            }
              else if(events[i].events & EPOLLOUT)
            {
                 //int count = send_cb(connfd);
                 int count = connlist[connfd].send_callback(connfd);
                 printf("sends:buffer->%s\n",connlist[connfd].wbuffer);
            }
        }

    }


getchar();
return 0;
   
}